package inference;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import probability.GaussianDistribution;
import probability.ProbabilityDistribution;
import basics.Vector;

public abstract class GraphNode {

	public String name;

	private String _nameWithoutIdx;

	public GraphNode(String varname) {
		this.name = varname;
		_nameWithoutIdx = getPureName(varname);
	}

	static String getPureName(String s) {
		while (Character.isDigit(s.charAt(s.length() - 1))) {
			s = s.substring(0, s.length() - 1);
		}

		return s;
	}

	public String toString() {
		return name;
	}

	public String pureName() {
		return _nameWithoutIdx;

	}

	public boolean is(String vname) {
		return pureName().equals(vname);
	}

	private boolean _observed = false;

	public void setObserved(boolean ob) {
		_observed = ob;
	}

	public boolean isObserved() {
		return _observed;
	}

	// private List<GraphNode> _parents = new ArrayList<GraphNode>();
	private HashMap<String, GraphEdge> _edges = new HashMap<String, GraphEdge>();

	public abstract BeliefMessage to(GraphNode next); // Vector

	public HashMap<String, GraphEdge> edges() {
		return _edges;
	}

	protected GraphEdge edges(String vname) {
		if (_edges.containsKey(vname)) {
			return _edges.get(vname);
		}

		for (String s : edges().keySet()) {
			if (getPureName(s).equals(vname)) {
				return edges().get(s);
			}
		}

		return null;
	}

	public abstract BeliefMessage toCached(GraphNode next);

	// { return to(next); }

	public abstract BeliefMessage toCached(GraphNode next, String opt);

	// { return to(next, opt); }

	// public List<Vector> parentFeatures(GraphNode to) {
	// ArrayList<Vector> f = new ArrayList<Vector>();
	// for (GraphEdge ee : edges().values()) {
	// for (GraphNode g : ee.nodes()) {
	// f.add((Vector) g.to(to).getValue());
	// }
	// }
	// return f;
	// }

	protected BeliefMessage innerTo(GraphNode next, boolean cached, String opt) {
		BeliefMessage msg = new BeliefMessage();
		ProbabilityDistribution g = null;
		for (GraphEdge e : edges().values()) {
			for (GraphNode f : e.nodes()) {
				if (f != next) { // 
					BeliefMessage m = null;
					if (cached && opt == null) {
						m = f.toCached(this);
					} else if (cached && opt != null) {
						m = f.toCached(this, opt);
					} else if (opt == null) {
						m = f.to(this);
					} else {
						m = f.to(this, opt);
					}
					if (g == null) {
						g = m.getDistribution();
					} else {
						g = g.times(m.getDistribution());
					}
					// VectorMatrix t = m.getValue();
					// if (_m.getValue() == null) {
					// _m.setValue(t);
					// } else if (t != null) {
					// _m.setValue(_m.getValue().times(t));
					// }
				}
			}
		}
		msg.setDistribution(g);
		return msg;
	}

	public ProbabilityDistribution marginal() {
		ProbabilityDistribution m = null;
		for (GraphEdge e : edges().values()) {
			for (GraphNode f : e.nodes()) {
				ProbabilityDistribution g = f.toCached(this).getDistribution();
				if (m == null) {
					m = g;
				} else {
					m = m.times(g);
				}
			}
		}

		return m;
	}

	public abstract BeliefMessage to(GraphNode next, String opt);

	public GaussianDistribution ep(GraphNode next) {
		if (next == null) // || next.toCached(this).getDistribution() == null
			return null;
		GaussianDistribution g = (GaussianDistribution) next.toCached(this).getDistribution(); //
		// toCached(null).getDistribution();
		GaussianDistribution m = (GaussianDistribution) marginal(); // new
																	// GaussianDistribution(g.getMu(),
		// g.getSigma2());
		// if (g == null) {
		// g = (GaussianDistribution) next.toCached(this).getDistribution();
		// } else {
		// g = (GaussianDistribution) g.times(next.toCached(this).getDistribution());
		// }

		// GaussianDistribution toThis = (GaussianDistribution) next.toCached(this).getDistribution();
		// m = (GaussianDistribution) g.times(toThis);

		g = m.divide(g);

		return g;
	}
}
